package cn.demoxy.service.impl;

import cn.demoxy.constant.CommonConstant;
import cn.demoxy.enums.ExceptionMessageEnum;
import cn.demoxy.exception.DemoException;
import cn.demoxy.listener.UserListener;
import cn.demoxy.mapper.DemoRoleMapper;
import cn.demoxy.mapper.DemoUserMapper;
import cn.demoxy.mapper.DemoUserRoleMapper;
import cn.demoxy.model.dto.DemoUserDTO;
import cn.demoxy.model.entity.DemoRole;
import cn.demoxy.model.entity.DemoTenant;
import cn.demoxy.model.entity.DemoUser;
import cn.demoxy.model.entity.DemoUserRole;
import cn.demoxy.model.vo.DemoUserVO;
import cn.demoxy.service.IDemoUserService;
import cn.demoxy.utils.JWTUtils;
import cn.demoxy.utils.RedisUtils;
import cn.demoxy.utils.SaltUtils;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.demoxy.service.impl.DemoServerConfServiceImpl.timeStampToDhms;

/**
 * @auther demo
 * @date 2022/5/17 21:39
 * 用户表业务实现
 */
@Service
@NoArgsConstructor
public class DemoUserServiceImpl extends ServiceImpl<DemoUserMapper,DemoUser> implements IDemoUserService {

    @Autowired
    private DemoUserRoleMapper userRoleMapper;

    @Autowired
    private DemoRoleMapper roleMapper;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    PaginationInterceptor paginationInterceptor;


    @Override
    public boolean saveUser(DemoUser user) {
        //生成随机盐
        String salt = SaltUtils.getSalt(8);
        //将随机盐保存到数据
        user.setSalt(salt);
        //设置为已激活
        user.setEnableFlag("0");
        //明文密码进行md5 + salt + hash散列加密
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
        user.setPassword(md5Hash.toHex());
        baseMapper.insert(user);
        user.getRoleIds().forEach(roleId -> {
            userRoleMapper.insert(new DemoUserRole().setUserId(user.getId()).setRoleId(roleId));
        });
        return true;
    }

    @Override
    public boolean update(DemoUser user) {
        baseMapper.updateById(user);
        if (user.getRoleIds().size()>0) {
            userRoleMapper.delete(new QueryWrapper<DemoUserRole>().eq("user_id", user.getId()));
            user.getRoleIds().forEach(roleId -> {
                userRoleMapper.insert(new DemoUserRole().setUserId(user.getId()).setRoleId(roleId));
            });
            return true;
        }
        return true;
    }

    @Override
    public Page<DemoUser> findAllOnlineUser(DemoUserDTO userDTO) {
        Page<DemoUser> page = new Page<>();
        List<DemoUser> userList = new ArrayList<>();
        long size = redisUtils.lGetListSize("user");
        for (long i = 0; i < size; i++) {
            Map<String, Object> onlineUser = (Map<String, Object>) redisUtils.lGetIndex("user", i);
            DemoUser user = findByUserName(onlineUser.get("username").toString());
            if (StringUtils.hasText(userDTO.getName())) {
               if (user.getName().contains(userDTO.getName())){
                   userList.add(user);
               }
            }
            if (!StringUtils.hasText(userDTO.getName())) {
                userList.add(user);
            }

        }
        page.setTotal(userList.size());
        List<DemoUser> user = userList.stream()
                .skip(userDTO.getSize() * (userDTO.getCurrent() - 1))
                .limit(userDTO.getSize()).collect(Collectors.toList());
        page.setRecords(user);
        page.setCurrent(userDTO.getCurrent());
        page.setSize(userDTO.getSize());

      return page;
    }

    @Override
    public boolean forcedReturnById(String username) {
        long size = redisUtils.lGetListSize("user");
        for (long i = 0; i < size; i++) {
            Map<String, Object> onlineUser = (Map<String, Object>) redisUtils.lGetIndex("user", i);
            if (onlineUser.get("username").equals(username)) {
                redisUtils.lRemove("user",1,onlineUser);
            }
        }
        return true;
    }
    @Override
    public Map<String, Object> login(DemoUserDTO userDTO) {
        // 创建SQL解析器集合
        List<ISqlParser> sqlParserList = new ArrayList<>();
        // 创建租户SQL解析器
        TenantSqlParser tenantSqlParser = new TenantSqlParser();

        // 设置租户处理器
        tenantSqlParser.setTenantHandler(new TenantHandler() {

            @Override
            public Expression getTenantId(boolean where) {
                // 设置当前租户ID，实际情况你可以从cookie、或者缓存中拿都行
                return new StringValue(userDTO.getTenantId());
            }

            @Override
            public String getTenantIdColumn() {
                // 对应数据库租户ID的列名
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                // 是否需要需要过滤某一张表,中间表无需进行租户过滤
                List<String> tableNameList = Arrays.asList("demo_tenant", "demo_role_menu", "demo_user_role");
                if (tableNameList.contains(tableName)) {
                    return true;
                }
                return false;
            }
        });
        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        DemoUser userObj = findByUserName(userDTO.getUsername());
        if (CommonConstant.USER_IS_DISABLE.equals(userObj.getEnableFlag())){
            throw new DemoException(ExceptionMessageEnum.USER_IS_DISABLE.getCode(),ExceptionMessageEnum.USER_IS_DISABLE.getMsg());

        }
        Map<String, Object> result = new HashMap<>();
        //登录验证账号密码
        if (userObj!=null) {
            String salt = userObj.getSalt();
            //明文密码进行md5 + salt + hash散列加密
            Md5Hash md5Hash = new Md5Hash(userDTO.getPassword(), salt, 1024);
            if (!md5Hash.toHex().equals(userObj.getPassword())){
                throw new AuthenticationException("用户名或密码错误");
            }
            Map<String, String> map = new HashMap<>();
            map.put("id",userObj.getId());
            map.put("username",userObj.getUsername());
            //根据用户id和用户名生成token
            long currentTimeMillis = System.currentTimeMillis();
            String token= JWTUtils.sign(userObj.getUsername(),currentTimeMillis);
            boolean flag = true;
            //查询redis中是否存在该用户,防止重复登录
            long size = redisUtils.lGetListSize("user");
            for (long i = 0; i < size; i++) {
                Map<String, Object> onlineUser = (Map<String, Object>) redisUtils.lGetIndex("user", i);
                if (onlineUser.get("username").equals(userDTO.getUsername())) {
                    flag = false;
                }
            }
            if (flag){
                Map<String, Object> item = new HashMap<>();
                item.put("username",userDTO.getUsername());
                item.put("ms",currentTimeMillis);
                redisUtils.lSet("user",item,60*30);
            }

            result.put("token",token);
            //根据用户id查询用户的角色
            List<DemoUserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<DemoUserRole>().eq("user_id", userObj.getId()));
            List<String> ids = new ArrayList<>();

            List<String> roleNames = new ArrayList<>();
            userRoles.forEach(userRole->{
                String roleId = userRole.getRoleId();
                ids.add(roleId);
                roleNames.add(roleMapper.selectById(roleId).getRoleName());
            });
            result.put("roleIds",ids);
            result.put("userId",userObj.getId());
            result.put("username",userObj.getUsername());
            result.put("roleName",roleNames);
            result.put("userFace",userObj.getUserFace());
        }else {
            throw new DemoException(ExceptionMessageEnum.USERNAME_IS_NOT_FOUND.getCode(),ExceptionMessageEnum.USERNAME_IS_NOT_FOUND.getMsg());
        }
        //用来存放payload
        return result;
    }

    @Override
    public Page<DemoUser> findAllUserByKeyword(DemoUserDTO userDTO) {
        //创建page对象，传递当前页，每页记录数
        Page<DemoUser> page = new Page<>(userDTO.getCurrent(), userDTO.getSize());
        //构建条件
        QueryWrapper<DemoUser> wrapper = new QueryWrapper<>();
        //根据用户名称查询
        if (StringUtils.hasText(userDTO.getUsername())) {
            wrapper.like("username", userDTO.getUsername());
        }
        //根据日期范围查询日志
        if (StringUtils.hasText(userDTO.getStartDate())&&StringUtils.hasText(userDTO.getEndDate())) {
            wrapper.ge("create_time",userDTO.getStartDate()).le("create_time",userDTO.getEndDate());
        }
        //调用方法实现分页查询
        Page<DemoUser> userPages = baseMapper.selectPage(page, wrapper);

        userPages.getRecords().forEach(user ->{
            List<DemoUserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<DemoUserRole>().eq("user_id", user.getId()));
            List<DemoRole> roles = new ArrayList<>();
            userRoles.forEach(userRole->{
                DemoRole demoRole = roleMapper.selectById(userRole.getRoleId());
                roles.add(demoRole);
            });
            user.setRoles(roles);
        });
        //返回结果
        return userPages;
    }

    @Override
    public DemoUser findByUserName(String username) {
        DemoUser user = baseMapper.selectOne(new QueryWrapper<DemoUser>().eq("username", username));
        //根据用户id查询用户的角色
        List<DemoUserRole> userRoles = userRoleMapper.selectList(new QueryWrapper<DemoUserRole>().eq("user_id", user.getId()));
        List<DemoRole> roles = new ArrayList<>();
        userRoles.forEach(userRole->{
            String roleId = userRole.getRoleId();
            DemoRole role = roleMapper.selectById(roleId);
            roles.add(role);
        });
        user.setRoles(roles);
        return user;
    }

    @Override
    public void exportData(HttpServletResponse response) {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码
            String fileName = URLEncoder.encode("用户管理", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
            List<DemoUser> userList = baseMapper.selectList(null);
            List<DemoUserVO> userVoList = new ArrayList<>(userList.size());
            for(DemoUser user : userList) {
                DemoUserVO demoUserVO = new DemoUserVO();
                BeanUtils.copyProperties(user, demoUserVO, DemoUserVO.class);
                userVoList.add(demoUserVO);
            }
            EasyExcel.write(response.getOutputStream(), DemoUserVO.class).sheet("用户管理").doWrite(userVoList);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void importUserData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(),DemoUserVO.class,new UserListener(baseMapper,userRoleMapper)).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
